package cn.miao.hrm.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.miao.client.LoginFeginClient;
import cn.miao.constant.BaseConst;
import cn.miao.constant.RedisConst;
import cn.miao.exception.MiaoRuntimeException;
import cn.miao.hrm.domain.Login;
import cn.miao.hrm.domain.VipBase;
import cn.miao.hrm.domain.VipUser;
import cn.miao.hrm.dto.RegisterUserDto;
import cn.miao.hrm.dto.SendSMSCode;
import cn.miao.hrm.mapper.VipBaseMapper;
import cn.miao.hrm.mapper.VipUserMapper;
import cn.miao.hrm.service.IVipUserService;
import cn.miao.hrm.to.SendPhoneCodeRedis;
import cn.miao.util.AjaxResult;
import cn.miao.util.BitStatesUtils;
import cn.miao.util.StrUtils;
import cn.miao.util.VerifyCodeUtils;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.invoke.util.VerifyType;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static cn.miao.constant.RedisConst.KEY_SEND_CODE_SMS_REGIST;
import static cn.miao.constant.RedisConst.KEY_SEND_IMG_CODE;
import static cn.miao.util.BitStatesUtils.OP_REGISTED;

/**
 * <p>
 * 会员登录账号 服务实现类
 * </p>
 *
 * @author MiaoDaWei
 * @since 2021-12-02
 */
@Service
@Slf4j
public class VipUserServiceImpl extends ServiceImpl<VipUserMapper, VipUser> implements IVipUserService {

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate<Object, Object> redisTemplate;
    @Autowired
    private LoginFeginClient loginFeginClient;
    @Autowired
    private VipBaseMapper vipBaseMapper;


    /**
     * 生成验证码
     *
     * @param key 关键
     * @return {@link AjaxResult}
     */
    @Override
    public AjaxResult createImgCode(String key) {
        //生成验证码
        String randomCode = StrUtils.getComplexRandomString(4);
        //生成图片验证码
        try {
            String imgCodeBase64=VerifyCodeUtils.verifyCode(150, 40, randomCode);
            //放进redis缓存中
            String redisKey = CharSequenceUtil.format(KEY_SEND_IMG_CODE, key);
            redisTemplate.opsForValue().set(redisKey, randomCode);
            //设置过期时间
            redisTemplate.expire(redisKey, 5L, TimeUnit.MINUTES);
            return AjaxResult.me().setResultObj(imgCodeBase64);
        } catch (IOException e) {
            e.printStackTrace();
            throw new MiaoRuntimeException("生成验证码错误!");
        }
    }

    /**
     * 发送短信代码
     *
     * @param dto dto
     * @return {@link AjaxResult}
     */
    @Override
    public AjaxResult sendSmsCode(SendSMSCode dto) {
        String mobile = dto.getMobile();
        String imageCode = dto.getImageCode();
        String imageCodeKey = dto.getImageCodeKey();

        //查询手机号是否已经被注册过了
        List<VipUser> phoneFromDb = this.selectByMap(MapUtil.of("phone", mobile));
        Assert.isTrue(CollUtil.isEmpty(phoneFromDb), "该手机号已被注册过,不可重复注册!");

        //判断验证码是否跟redis中的key的验证码是否保持一致!
        String redisKey = CharSequenceUtil.format(KEY_SEND_IMG_CODE, imageCodeKey);
        String imgCodeFromRedis = (String) redisTemplate.opsForValue().get(redisKey);
        Assert.isTrue(CharSequenceUtil.isNotEmpty(imgCodeFromRedis), "无效的图片验证码!");
        if (!imgCodeFromRedis.equalsIgnoreCase(imageCode)) throw new MiaoRuntimeException("图片验证码错误");

        //发送短信
        DateTime currentDataTime = DateUtil.date();
        String redisPhoneKey = CharSequenceUtil.format(KEY_SEND_CODE_SMS_REGIST, mobile);

        String phoneCode;
        //先从redis缓存中查看是否存在
        SendPhoneCodeRedis phoneCodeFromRedis = (SendPhoneCodeRedis) redisTemplate.opsForValue().get(redisPhoneKey);
        if (BeanUtil.isNotEmpty(phoneCodeFromRedis)) {
            //获取存放的时间,判断是否跟当前时间处于1分钟以内,如果有则认为这是处于恶意频繁发送短信
            boolean canSendCode = DateUtil.between(currentDataTime, phoneCodeFromRedis.getCurrentTime(), DateUnit.MINUTE, false) < 1;
            Assert.isTrue(canSendCode, "请勿频繁发送短信验证码");
            //使用redis缓存中还存在的验证码发送
            phoneCode = phoneCodeFromRedis.getCode();
        }else{
            phoneCode = StrUtils.getRandomString(6);
        }

        log.info("发送短信成功,验证码:{},有效时间为:{}分钟", phoneCode, 5);
        //存储到redis缓存中
        redisTemplate.opsForValue().set(redisPhoneKey, new SendPhoneCodeRedis().setCode(phoneCode).setCurrentTime(currentDataTime));
        //设置有效过期时间
        redisTemplate.expire(redisPhoneKey, 5L, TimeUnit.MINUTES);
        //删除图片验证码的redis的key
        redisTemplate.delete(imageCodeKey);
        // TODO: 2021/12/2 这里可以利用消息公共服务发送短信,然后发送完将信息保存保存到数据库表中!
        return AjaxResult.me();
    }

    /**
     * 注册
     *
     * @param dto dto
     * @return {@link AjaxResult}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult register(RegisterUserDto dto) {
        //查询手机号是否已经被注册过了
        List<VipUser> phoneFromDb = this.selectByMap(MapUtil.of("phone", dto.getMobile()));
        Assert.isTrue(CollUtil.isEmpty(phoneFromDb), "该手机号已被注册过,不可重复注册!");

        //校验短信验证码在redis端是否一致
        String smsPhoneKeyFromRedis = CharSequenceUtil.format(KEY_SEND_CODE_SMS_REGIST, dto.getMobile());
        SendPhoneCodeRedis phoneCodeRedis = (SendPhoneCodeRedis) redisTemplate.opsForValue().get(smsPhoneKeyFromRedis);
        Assert.isTrue(BeanUtil.isNotEmpty(phoneCodeRedis), "验证码已过期,请重新获取!");
        if (!phoneCodeRedis.getCode().equalsIgnoreCase(dto.getSmsCode())) throw new MiaoRuntimeException("短信校验码错误!");

        //保存login表
        Login login = new Login();
        login.setUsername("行走的绿巨人");
        login.setPassword(dto.getPassword());
        login.setType(BaseConst.LoginState.STATUS_TYPE_USER);
        AjaxResult result = loginFeginClient.save(login);
        Assert.isTrue(result.isSuccess() || Objects.nonNull(result.getResultObj()), "保存登录失败");

        //保存t_vip_user
        Date date = new Date();
        VipUser user = new VipUser();
        user.setCreateTime(date.getTime());
        user.setPhone(dto.getMobile());
        user.setPassword(dto.getPassword());
        user.setNickName(dto.getMobile());
        long state = BitStatesUtils.addState(user.getBitState(), OP_REGISTED);
        long addState = BitStatesUtils.addState(state, BitStatesUtils.OP_AUTHED_PHONE);
        user.setBitState(addState);
        user.setLoginId(Convert.toLong(result.getResultObj()));
        this.insert(user);

        //保存t_vip_base
        VipBase vipBase = new VipBase();
        vipBase.setCreateTime(date.getTime());
        vipBase.setSsoId(user.getId());
        vipBase.setRegChannel(dto.getRegChannel());
        vipBase.setRegTime(date.getTime());
        vipBaseMapper.insert(vipBase);

        //删除手机验证码在redis中
        redisTemplate.delete(smsPhoneKeyFromRedis);
        return AjaxResult.me();
    }
}
